package ee.objectCloner.impl.cloners;

import java.util.Collection;

import ee.metadataUtils.ObjectFactory;
import ee.metadataUtils.PropertyMetadata;
import ee.metadataUtils.PropertyMetadataRegistry;
import ee.objectCloner.CustomCloner;
import ee.objectCloner.ObjectCloner;

public class DefaultCloner implements CustomCloner {

	private PropertyMetadataRegistry _propertyMetadataRegistry;
	private ObjectFactory _objectFactory;

	public DefaultCloner(ObjectFactory objectFactory,
			PropertyMetadataRegistry propertyMetadataRegistry) {
		_objectFactory = objectFactory;
		_propertyMetadataRegistry = propertyMetadataRegistry;
	}

	@Override
	public Object clone(Object source, ObjectCloner objectCloner) {
		Object destination;
		Class<? extends Object> type = source.getClass();
		try {
			destination = _objectFactory.create(type);
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		}

		Collection<PropertyMetadata> propertyMetadataCollection = _propertyMetadataRegistry
				.getPropertyMetadataCollection(type);

		try {
			for (PropertyMetadata propertyMetadata : propertyMetadataCollection) {
				propertyMetadata.setValue(destination,
						objectCloner.clone(propertyMetadata.getValue(source)));
			}
		} catch (Throwable e) {
			throw new RuntimeException(e);
		}

		return destination;
	}

	@Override
	public void copy(Object source, Object destination,
			ObjectCloner objectCloner) {
		if (source.equals(destination)) {
			return;
		}

		Class<? extends Object> type = source.getClass();

		Collection<PropertyMetadata> propertyMetadataCollection = _propertyMetadataRegistry
				.getPropertyMetadataCollection(type);

		try {
			for (PropertyMetadata propertyMetadata : propertyMetadataCollection) {
				propertyMetadata.setValue(destination, objectCloner
						.copyOrClone(propertyMetadata.getValue(source),
								propertyMetadata.getValue(destination)));
			}
		} catch (Throwable e) {
			throw new RuntimeException(e);
		}

	}

	@Override
	/**
	 * Always returns true, but will fail on certain types (these should be caught by custom 
	 * converters or the object cloner itself.
	 */
	public boolean canCopy(Class<?> sourceType, Class<?> destinationType) {
		return true;
	}

	@Override
	/**
	 * Always returns true, but will fail on certain types (these should be caught by custom 
	 * converters or the object cloner itself.
	 */
	public boolean canClone(Class<?> sourceType) {
		return true;
	}


	
}
